
open Printf
open ExtLib
open Array
open Sys
open Unix

open Learner
open Randif
open Bmap
open Banking 
open Constants
open Types
open World2
open Management 
open Managementstore;;


let seed = [1;2;3;4;5]

(* minliq, mincap *)
let testRegulator = (0.03,0.04)

let testPolicyLoan = 
    (PIf (
          (PNumTest (PRate,PGT,(PNum (0.09 *. rateScale)))),
          (PIf ((PNumTest (PCapitalRat,PGT,(PNum (0.4 *. capScale)))),
                (PIf ((PNumTest (PBankCash,PGT,PPrincipal)),
                      (PNumTest (PLiqRat,PGT,(PNum (0.5 *. liqScale)))), (* keep a min liq assets of 15% *)
                      PReject)),
		PReject)),
           PReject
	 )
    )

let testPolicyDep = 
    (PIf ((PNumTest (PRate,PLT,(PNum (0.02 *. rateScale)))),
         PAccept,
         PReject))


let longDatPolicyLoan =  (* more assets than liabs - lend! *) 
  (PNumTest ((PBSGap (PNum 3.,PNum 10.)),PGT,PNum 0.))

let testMgtRegs = []

let testBorrower = { defaultDist = ConstantPD 0.01;
		     principalDist = UniformPD (10.,20.);
		     rateStep = ConstantPD 0.01;
		     rateStart = ConstantPD 0.0;
		     maxOfferSteps = 10;
		     durationDist = UniformPD (1.,5.);
		     recoveryDist = ConstantPD 0.5;
		     offeror = Borrower }

let testLender = { defaultDist = ConstantPD 0.01;
		   principalDist = UniformPD (10.,20.);
		   rateStep = ConstantPD (- 0.01);
		   rateStart = ConstantPD 0.10;
		   maxOfferSteps = 10;
		   durationDist = UniformPD (1.,5.);
		   recoveryDist = ConstantPD 0.5;
		   offeror = Lender }

let testShortBorrower = { defaultDist = ConstantPD 0.01;
			  principalDist = UniformPD (10.,20.);
			  rateStep = ConstantPD 0.01;
			  rateStart = ConstantPD 0.0;
			  maxOfferSteps = 10;
			  durationDist = UniformPD (0.,1.);
			  recoveryDist = ConstantPD 0.5;
			  offeror = Borrower }

let testShortLender = { defaultDist = ConstantPD 0.01;
			principalDist = UniformPD (10.,20.);
			rateStep = ConstantPD (- 0.01);
			rateStart = ConstantPD 0.10;
			maxOfferSteps = 10;
			durationDist = UniformPD (0.,1.);
			recoveryDist = ConstantPD 0.5;
			offeror = Lender }



let testManagement = SimpleM { lenderPolicy =PReject;
			       borrowerPolicy=PReject;
			       dividendPolicy=(PNum 0.1);
			       registers = testMgtRegs }


let testManagement2 = SimpleM { lenderPolicy =testPolicyLoan;
				borrowerPolicy= testPolicyDep;
				dividendPolicy=(PNum 0.1);
				registers = testMgtRegs }


let bayes_borrower = { defaultDist = ConstantPD 0.01;
		     principalDist = UniformPD (10.,20.);
		     rateStep = ConstantPD 0.01;
		     rateStart = UniformPD (0.03,0.1);
		     maxOfferSteps = 10;
		     durationDist = UniformPD (0.01,5.);
		     recoveryDist = ConstantPD 0.5;
		     offeror = Borrower }

let bayes_lender = { defaultDist = ConstantPD 0.01;
		   principalDist = UniformPD (10.,20.);
		   rateStep = ConstantPD (- 0.01);
		     rateStart = UniformPD (0.03,0.1);
		   maxOfferSteps = 10;
		   durationDist = UniformPD (0.01 ,5.);
		   recoveryDist = ConstantPD 0.5;
		   offeror = Lender }


let test_lender_tests = [
  PNumTest (PLiqRat, PGT, (PNum 0.05)) ;
  PNumTest (PCapitalRat, PGT, (PNum 0.05)) ;
  PNumTest (PBankCash, PGT, (PNum 0.001))
  (* PNumTest (PCashRate, PGT, (PNum 0.01)) *)
]


let test_borrower_tests = [
  PNumTest (PLiqRat, PGT, (PNum 0.05)) ;
  PNumTest (PCapitalRat, PGT, (PNum 0.05)) ;
  PNumTest (PBankCash, PGT, (PNum 0.001))
  (* PNumTest (PCashRate, PGT, (PNum 0.01)) *)
]


let testTabManagement =
  let rateb = [ 0.01; 0.02; 0.03; 0.04; 0.05; 0.06;0.07;0.08;0.09] in
  let matb = [ 0.08; 0.16; 0.33; 0.5; 0.66; 0.84 ; 1.; 2.; 3.; 4.; 5.; 7.; 10. ] in
  let rawtab = new_table_management rateb matb in
  let rix = 0 -- ((List.length rateb)) in
  let mix = 0 -- ((List.length matb)) in
  List.iter (fun r -> List.iter (set_table_management Lender rawtab PAccept r) mix) rix;
  List.iter (fun r -> List.iter (set_table_management Borrower rawtab PAccept r) mix) rix;
  TableM { rawtab with
	   lender_tests = test_lender_tests;
	   borrower_tests = test_borrower_tests }


let testTabManagement2 =
  let rateb = [ 0.01; 0.02; 0.03; 0.04; 0.05; 0.06;0.07;0.08;0.09] in
  let matb = [ 0.08; 0.16; 0.33; 0.5; 0.66; 0.84 ; 1.; 2.; 3.; 4.; 5.; 7.; 10. ] in
  let racceptl = 6 -- 9 in (* LENDER == WHOLESALE FUNDING == LOW RATES *)
  let racceptb = 0 -- 4 in (* BORROWER == WRITING NEW LOAN == HIGH RATES *)
  let rawtab = new_table_management rateb matb in
  let rix = 0 -- ((List.length rateb)) in
  let mix = 0 -- ((List.length matb)) in
  List.iter (fun r -> List.iter (set_table_management Lender rawtab PReject r) mix) rix;
  List.iter (fun r -> List.iter (set_table_management Borrower rawtab PReject r) mix) rix;
  List.iter (fun r -> List.iter (set_table_management Lender rawtab PAccept r) mix) racceptb;
  List.iter (fun r -> List.iter (set_table_management Borrower rawtab PAccept r) mix) racceptl;
  TableM  { rawtab with
	    lender_tests = test_lender_tests;
	    borrower_tests = test_borrower_tests }

let test_vector_management =
  let matb = [ 0.08; 0.16; 0.33; 0.5; 0.66; 0.84 ; 1.; 2.; 3.; 4.; 5.; 7.; 10. ] in
  let matl = List.length matb in
  (* let lender = ExtList.make matl 0.06 in
  let borrower = ExtList.make matl 0.03 in *)
  VectorM {
  vec_maturity_breaks = Array.of_list matb;
  vec_maturity_extent = matl;
  vec_div_rate = (PNum 0.05);
  lender_vec = Array.make (matl + 1) 0.06;
  borrower_vec = Array.make (matl+1) 0.03 }
    
let startCash = 100.0


let testBond = Bond { start = zeroDate;
                      maturity = {year = 1;month=0};
                      rate = 0.07;
                      principal = (10.0);
                      rdefault = 0.01;
                      recovery = 0.5 }


let startBonds = [testBond ; testBond ; testBond ]

let testBank = updateCache
               { management=empty_bayes_management;
		 bankcash=startCash;
		 assets=colCon startBonds;
		 liabilities=colCon [];
		 deposits = valBL startBonds;
		 depositRate=0.0 ;
		 regulator_control=false ;
		 dividends=[];
		 profit=[] ;
		 costs = 0;
		 accounts = 0;
		 bankID = 0;
		 book_equity = 0.; (* note next 3 done in cache *)
		 capital_ratio = 0.;
		 liquid_ratio = 0.;
		 failTime=12.0; 
		 failPenalty=(-1.0); (* -- per year of insolvency *)
	         accepted_lender_contracts=Bmap.empty cs_max prof_thresh;
	         accepted_borrower_contracts=Bmap.empty cs_max prof_thresh;
		 rejected_lender_contracts=Bmap.empty cs_max prof_thresh;
		 rejected_borrower_contracts=Bmap.empty cs_max prof_thresh;
	       } 

let end_date = {year = 20; month = 0}

let short_end_date = {year=5; month=0}

let testWorld = { banks = List.map2 (fun b i -> {b with bankID = i} ) (replicate numbanks testBank) (1 -- numbanks);
		  wdate = zeroDate ;
		  enddate = end_date;
		  lenders = (replicate 15 testLender )
		           @ (replicate 25 testShortLender);
		  borrowers = (replicate 15 testBorrower )
		           @ (replicate 25 testShortBorrower);
		  regulator = testRegulator;
		  cash_rate_params = crp;
		  diagnostic = () }

(* TODO not used yet *)
let testMutator = {
  mutNum = ConstantPD 0.0;
  mutTree = ConstantPD 0.0;
  mutTableCross = ConstantPD 0.0;
  mutTablePoint = ConstantPD 0.0 }

let testSim = { maxSteps = 2000 ;
		currentSteps = 1;
		retentionBound = numbanks / 2;
		mutator = testMutator;
		bestManagement = replicate numbanks testTabManagement2; 
		mgtStore = empty_management_store;
		simid = 1;
	        random_state = Random.State.make (Array.of_list seed) }

let bayes_world = 
  { banks = List.map2 (fun b i -> {b with bankID = i} ) 
      (replicate numbanks testBank) (1 -- numbanks);
    wdate = zeroDate ;
    enddate = end_date;
    lenders = (replicate 15 bayes_lender ) ;
    borrowers = (replicate 15 bayes_borrower ) ;
    regulator = testRegulator;
    cash_rate_params = crp;
    diagnostic = () }


let bayes_sim = 
  { maxSteps = 3;
    currentSteps = 1;
    retentionBound = numbanks / 2;
    mutator = testMutator;
    bestManagement = replicate numbanks empty_bayes_management; 
    mgtStore = empty_management_store;
    simid = 1;
    random_state = Random.State.make (Array.of_list seed) }



let gparams = { checkpoint_frequency = 50;
		longoutput_frequency = 25;
		fileroot = (sprintf "%s/brun-%010.2f/" (Sys.getcwd ()) 
			      (Unix.time ()));
	        debug = false }

let maint () =
  let nb = nStepBank 15 zeroDate testBank in
  print_string ((print_bank nb)^"\n") 

let testbm bm str (rtl:float list) =
  let rtc (rt:float) = 
    (let (rr:float) = (hiprof bm cs_rate rt) in
     print_string (str^" "^(Std.dump rt)^": "^(Std.dump rr)^"\n")) in
  List.iter rtc rtl
  

let mainw () = 
  let resW = n_step_world true bayes_world in
  print_string (bsummary 1 0 resW);
  print_string (Bmap.dump (List.hd (resW.banks)).accepted_lender_contracts);
  let (BayesM nmgt) = construct_bmaps 0.0 resW.banks in
  print_string (Bmap.dump (nmgt.accepted_lenders));
  let (testlkupal:float) = (hiprof nmgt.accepted_lenders cs_rate 0.07) in
  let (testlkuprl:float) = (hiprof nmgt.rejected_lenders cs_rate 0.07) in
  print_string ("accepted lenders lookup: "^(Std.dump testlkupal)^"\n");
  print_string ("rejected lenders lookup: "^(Std.dump testlkuprl)^"\n");
  testbm  nmgt.accepted_lenders "accepted l" [0.01;0.02;0.03;0.04;0.05;0.06;0.07;0.08;0.09;0.10] ;
  testbm  nmgt.rejected_lenders "rejected l" [0.01;0.02;0.03;0.04;0.05;0.06;0.07;0.08;0.09;0.10] ;
  testbm  nmgt.accepted_borrowers "accepted b" [0.01;0.02;0.03;0.04;0.05;0.06;0.07;0.08;0.09;0.10] ;
  testbm  nmgt.rejected_borrowers "rejected b" [0.01;0.02;0.03;0.04;0.05;0.06;0.07;0.08;0.09;0.10] ;
  print_string "\n"

let main () = 
  Unix.mkdir gparams.fileroot 0o777;
  (* print_string bsummary_header; *)
  let nsps = ( manyWorlds bayes_sim bayes_world gparams ) in
  print_endline (print_bayes_management (List.hd nsps.bestManagement));;
  (* printf "Done %d\n" nsps.simid ;; *)

main () ;;

(*

let mainlt () = 
  print_string (Std.dump (set_nth 2 100 [5;6;7;8;9]))

;;

mainlt () ;;
*)
